# -*- coding: utf-8 -*-
import logging
import random
import time

import netaddr
import requests

from functools import wraps

from flask import request, jsonify

from . import db
from . import gen_url
from .endpoints import urls

LOGGER = logging.getLogger(__name__)


def curtime():
    return time.strftime('%Y-%m-%d %H:%M:%S')


def admin_only(fn):
    @wraps(fn)
    def wrapper(*a, **kw):
        user_name = request.headers['user_name']
        if not is_admin(user_name):
            return jsonify(status=403, msg='fail', data=u'您不是高级用户!')
        else:
            return fn(*a, **kw)
    return wrapper


def list_tokens(api_version=None):
    LOGGER.debug('list token: %s', api_version)
    return db.get('token', where={'api_version': api_version})


def get_token(cluster_id):
    url = gen_url('token_service')
    params = {
        'cluster_id': cluster_id,
    }
    r = requests.get(url, params=params)
    if r.ok:
        return 0, r.json()
    else:
        return 1, r.content or r.reason


# CDN
def list_cdn_log(stime, etime, limit_start, limit_end):
    url = gen_url('cdn_log_search', host=urls['cdn_log_search_host'])
    params = {
        'query_start_time': stime,
        'query_end_time': etime,
        'limit_start': limit_start,
        'limit_end': limit_end,
    }
    r = requests.post(url, json=params)
    if r.ok:
        data = r.json()
        return data['status'], data['content']
    else:
        return 1, r.content or r.reason


def get_cdn_submit_stats(project_name, period_type, start_time, q):
    url = gen_url('cdn_submit_'+q, host=urls['cdn_submit_host'])
    params = {
        'project_name': project_name,
        'period_type': period_type,
        'start_time': start_time,
    }
    r = requests.post(url, data=params)
    if r.ok:
        return 0, r.json()
    else:
        return 1, r.content or r.reason


def search_cdn_log(project_name, file_name, status,
                   download_url, start_time, page, page_size):

    url = gen_url('cdn_search', host=urls['cdn_submit_host'])
    params = {
        'project_name': project_name,
        'file_name': file_name,
        'status': status,
        'download_url': download_url,
        'start_time': start_time,
        'from': page,
        'size': page_size,
    }
    LOGGER.debug('search cdn log: %r', params)
    r = requests.post(url, data=params)
    if r.ok:
        return 0, r.json()
    else:
        return 1, r.content or r.reason


def get_cdn_log_detail(task_id, status):
    url = gen_url('cdn_log_detail', host=urls['cdn_log_detail_host'])
    params = {
        'task_id': task_id,
        'status': status,
    }
    r = requests.get(url.format(**params))
    if r.ok:
        data = r.json()
        return data['status'], data['content']
    else:
        return 1, r.content or r.reason


# Project
def get_self_projects(user_id):
    url = gen_url('self_projects')
    r = requests.get(url, params={'user_id': user_id})
    if r.ok:
        data = r.json()
        return data['status'], data['message']
    else:
        return 1, r.content or r.reason


def get_co_projects(user_id):
    url = gen_url('co_projects')
    r = requests.get(url, params={'user_id': user_id})
    if r.ok:
        data = r.json()
        return data['status'], data['message']
    else:
        return 1, r.content or r.reason


def delete_project(project_id, user_id):
    url = gen_url('project_del')
    values = {
        'user_id': user_id,
        'project_id': project_id,
    }
    r = requests.post(url, data=values)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 2, r.content or r.reason
    return resp


def set_default_project(user_name, project_id):
    url = gen_url('default_project')
    params = {
        'user_id': user_name,
        'project_id': project_id,
    }
    r = requests.post(url, data=params)
    if r.ok:
        data = r.json()
        return data['status'], data['message']
    else:
        return 1, r.content or r.reason


# Cluster
def get_project_clusters(project_id, revert=0):
    if revert:
        url = gen_url('unattached_clusters')
    else:
        url = gen_url('attached_clusters')

    r = requests.get(url, params={'project_id': project_id})
    if r.ok:
        data = r.json()
        return data['status'], data['message']
    else:
        return 1, r.content or r.reason


def attach_cluster_to_project(project_id, cluster_id):
    url = gen_url('attach_cluster')
    data = {'project_id': project_id, 'cluster_id': cluster_id}
    r = requests.post(url, data=data)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


def dettach_cluster_from_project(project_id, cluster_id):
    url = gen_url('deattach_cluster')
    data = {'project_id': project_id, 'cluster_id': cluster_id}

    r = requests.post(url, data=data)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


# Project
def new_project(username, project_name):
    url = gen_url('project_add')

    data = {
        'user_id': username,
        'project_name': project_name,
    }
    r = requests.post(url, data=data)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


# Instance
def create_instance(values, version=1):
    endpoint = 'instance_create' if version == 1 else 'instance_create_v2'
    url = gen_url(endpoint)
    r = requests.post(url, data=values)
    if r.ok:
        resp = 0, r.json()
    else:
        resp = r.status_code, r.content or r.reason
    return resp


def save_record_v2(instance, cluster_id, image_id,
                   flavor_id, create_time, project_id,
                   user, **cfg):
    values = {
        'instance_id': instance.id,
        'vm_name': instance.name,
        'cluster_id': cluster_id,
        'image_id': image_id,
        'flavor_id': flavor_id,
        'az': 'nova',
        'status': 'creating',
        'user_id': user,
    }
    if instance.is_linux():
        values.update({
            'key_name': instance.key_name,
            'create_time': create_time,
            'project_id': project_id,
        })

    values.update(cfg)
    return db.add('instance', values=values)


def list_project_instances(project_id, excludes='deleted'):
    url = gen_url('instances_list') + '/' + project_id
    params = {
        'excludes': excludes,
    }
    r = requests.get(url, params=params)
    if r.ok:
        data = r.json()
        status = 0 if data['status'] == 'ok' else 1
        resp = status, data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


def get_instance_info(instance_id):
    url = gen_url('instance_q_v2') + '/' + instance_id
    r = requests.get(url)
    if r.ok:
        data = r.json()
        status = 0 if data['status'] == 'ok' else 1
        resp = status, data['message']
    else:
        resp = 1, r.content or r.reason

    return resp


def patch_instance(instance_id, user_id, tag):
    url = gen_url('instance_tag')
    params = {
        'instance_id': instance_id,
        'user_id': user_id,
        'tag': tag,
    }
    r = requests.post(url, data=params)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


def get_vnc(cluster_id, instance_id):
    url = gen_url('vnc').format(instance_id=instance_id)
    params = {'cluster_id': cluster_id}
    r = requests.get(url, params=params)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


def search_instance_in_project(project_id, search_k, search_v):
    url = gen_url('instances_search').format(project_id=project_id)
    params = {
        'excludes': 'deleted',
        search_k: search_v,
    }
    r = requests.get(url, params=params)
    if r.ok:
        data = r.json()
        return data['status'], data['message']
    else:
        return 1, r.content or r.reason


def simplify_monitor_data(metric, raw_data):
    keys = sorted(raw_data.keys())
    if metric == 'memory.usage':
        data = [float('{:.2f}'.format(raw_data[k]/1024/1024)) for k in keys]

    elif metric in ('network.incoming.bits.rate',
                    'network.outgoing.bits.rate'):
        data = [float('{:.2f}'.format(raw_data[k]/1024/1024)) for k in keys]

    elif metric == 'disk.read.bytes.rate' or metric == 'disk.write.bytes.rate':
        data = [float('{:.2f}'.format(raw_data[k]/1024)) for k in keys]

    elif metric == 'cpu.idle':
        data = [float('{:.2f}'.format(100-raw_data[k])) for k in keys]

    else:
        data = [float('{:.2f}'.format(raw_data[k])) for k in keys]
    return keys, data


def get_instance_health_data(instance_id, metric, stime, etime):
    url = gen_url('instance_health')
    params = {
        'start': stime,
        'end': etime,
        'instance_id': instance_id,
        'metric': metric,
    }
    r = requests.post(url, data=params)
    if r.ok:
        data = r.json()

        if data['status']:
            resp_data = data['message']
        else:
            keys, datas = simplify_monitor_data(metric, data['data']['dps'])
            resp_data = {'keys': keys, 'datas': datas}

        resp = data['status'], resp_data
    else:
        resp = 1, r.content or r.reason

    return resp


def get_instance_oplog(instance_id, page_size, offset):
    url = gen_url('instance_oplog_list')
    params = {
        'resource_id': instance_id,
        'size': page_size,
        'offset': offset,
    }
    r = requests.get(url, params=params)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason

    return resp


def destroy_instance(instance_id, cluster_id):
    s, m = get_instance_info(instance_id)
    if s:
        return s, m

    url = gen_url('instance_destroy')
    params = {
        'cluster_id': cluster_id,
        'instance_id': instance_id,
    }
    r = requests.post(url, data=params)
    if r.ok:
        data = r.json()
        status = 0 if data['status'].lower() == 'ok' else 1
        resp = status, 'Delete %s: %s!' % (instance_id, data['message'])
    else:
        resp = 1, r.content or r.reason
    return resp


def start_instance(instance_id, cluster_id):
    url = gen_url('instance_start')
    params = {
        'instance_id': instance_id,
        'cluster_id': cluster_id,
    }
    r = requests.post(url, data=params)
    if r.ok:
        data = r.json()
        status = 0 if data['status'] == 'SUCCESS' else 1
        print 'start resp: %s' % data
        resp = status, data['result']
    else:
        resp = 1, r.content or r.reason
    return resp


def stop_instance(instance_id, cluster_id):
    url = gen_url('instance_stop')
    params = {
        'instance_id': instance_id,
        'cluster_id': cluster_id,
    }
    r = requests.post(url, data=params)
    if r.ok:
        data = r.json()
        status = 0 if data['status'] == 'SUCCESS' else 1
        resp = status, data['result']
    else:
        resp = 1, r.content or r.reason
    return resp


def restart_instance(instance_id, cluster_id, is_hard=1):
    url = gen_url('instance_reboot')
    params = {
        'instance_id': instance_id,
        'cluster_id': cluster_id,
        'is_hard': is_hard,
    }
    r = requests.post(url, data=params)
    if r.ok:
        data = r.json()
        print 'restart data: %s' % data
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


# new operate instances
def operate_instance(instance_id, cluster_id, action, is_hard='true'):
    url = gen_url('instance_operate').format(instance_id=instance_id)
    # http://ip:port/status_helper/instances/{instance_id}/action
    params = {
        'cluster_id': cluster_id,
        'action': action,
        'is_hard': is_hard,
    }
    r = requests.post(url, data=params)
    if r.ok:
        data = r.json()
        print 'restart data: %s' % data
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


# new destroy instance
def delete_instance(instance_id, cluster_id):
    url = gen_url('instance_operate').format(instance_id=instance_id)
    # http://ip:port/status_helper/instances/{instance_id}/action
    params = {
        'cluster_id': cluster_id,
    }
    r = requests.delete(url, data=params)
    if r.ok:
        data = r.json()
        print 'restart data: %s' % data
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


# Instance.status
def instance_status_create(values):
    LOGGER.debug('instance create status: %s', values)

    url = gen_url('status_helper')
    r = requests.post(url, data=values)
    push_log_all(data=values, action='create')

    if r.ok:
        data = r.json()
        resp = 0, data,
    else:
        LOGGER.error(r.content)
        resp = 1, r.content or r.reason

    LOGGER.debug("store in DB %s: %s" % resp)
    return resp


def instance_status_update(action, instance_id, cluster_id):
    url = gen_url('status_helper_update').format(action=action)
    print 'status update: %s' % url
    params = {
        'instance_id': instance_id,
        'cluster_id': cluster_id,
    }
    r = requests.post(url, data=params)
    if r.ok:
        # FIXME: 这里需要接口返回值说明
        # data = r.json()
        # resp = data['status'], data['message']
        resp = 0, r.content
    else:
        resp = 1, r.content or r.reason
    return resp


# Memebers
def get_member_in_project(user_id, project_id):
    url = gen_url('members_in_project')
    params = {'user_id': user_id, 'project_id': project_id}
    r = requests.get(url, params=params)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


def get_member_not_in_project(user_id, project_id):
    url = gen_url('members_not_in_project')
    params = {'project_id': project_id, 'user_id': user_id}
    r = requests.get(url, params=params)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


def get_all_members(user_id):
    url = gen_url('members_list')
    params = {'owner_id': user_id}
    r = requests.get(url, params=params)
    if r.ok:
        data = r.json()
        return data['status'], data['message']
    else:
        return 1, r.content or r.reason


def freeze_member(owner_id, member_id):
    url = gen_url('members_freeze').strip('/')
    r = requests.post(url, data={'member_id': member_id, 'owner_id': owner_id})
    if r.ok:
        data = r.json()
        return data['status'], data['message']
    else:
        return 1, r.content or r.reason


def unfreeze_member(owner_id, member_id):
    url = gen_url('members_unfreeze').strip('/')
    print 'unfreeze url: %s' % url
    r = requests.post(url, data={'member_id': member_id, 'owner_id': owner_id})
    if r.ok:
        data = r.json()
        return data['status'], data['message']
    else:
        return 1, r.content or r.reason


def cancel_member(owner_id, member_id):
    url = gen_url('members_delete').strip('/')
    r = requests.post(url, data={'member_id': member_id, 'owner_id': owner_id})
    if r.ok:
        data = r.json()
        return data['status'], data['message']
    else:
        return 1, r.content or r.reason


def invite_member_by_email(owner, email):
    url = gen_url('members_add')
    r = requests.post(url, data={'email': email, 'owner_id': owner})
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


def attach_member_to_project(user_name, project_id, member_id, role_id):
    url = gen_url('attach_member_to_project')
    data = {
        'user_id': user_name,
        'member_id': member_id,
        'project_id': project_id,
        'role_id': role_id,
    }
    r = requests.post(url, data=data)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


def dettach_member_from_project(user_name, project_id, member_id):
    url = gen_url('dettach_member_from_project')
    data = {
        'user_id': user_name,
        'member_id': member_id,
        'project_id': project_id,
    }
    r = requests.post(url, data=data)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, u'移除协作者失败: %s!' % r.content or r.reason
    return resp


# Role
def list_roles_by_creator(creator):
    url = gen_url('roles')

    r = requests.get(url, params={'owner_id': creator})
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


def delete_role(role_id):
    url = gen_url('roles') + '/' + role_id
    r = requests.delete(url)
    if r.ok:
        data = r.json()
        resp = data['status'], data['message']
    else:
        resp = 1, r.content or r.reason
    return resp


def add_single_role(user_id, role_name):
    url = gen_url('roles')
    r = requests.post(url, data={'owner_id': user_id, 'role_name': role_name})
    if r.ok:
        data = r.json()
        if data['status']:
            return 1, data['message']
        else:
            return 0, data['message']['role_id']
    else:
        return 2, r.content or r.reason


def new_role_permissions(role_id, **permissions):
    url = gen_url('roles') + '/' + role_id + '/privileges'
    print 'set permission: %s' % url
    print 'permission: %s' % permissions
    r = requests.post(url, json=permissions)
    if r.ok:
        data = r.json()
        if data['status']:
            return 1, data['message']
        else:
            return 0, data['message']
    else:
        return 2, r.content or r.reason


def update_role_permissions(role_id, **permissions):
    url = gen_url('roles') + '/' + role_id + '/privileges'
    print 'set permission: %s' % url
    print 'permission: %s' % permissions
    r = requests.put(url, json=permissions)
    if r.ok:
        data = r.json()
        if data['status']:
            return 1, data['message']
        else:
            return 0, data['message']
    else:
        return 2, r.content or r.reason


def get_role_profile(role_id):
    url = gen_url('roles') + '/' + role_id
    print url
    r = requests.get(url)
    if r.ok:
        data = r.json()
        if data['status']:
            return 1, data['message']
        else:
            return 0, data['message']
    else:
        return 2, r.content or r.reason


def get_role_permissions(role_id):
    url = gen_url('roles') + '/' + role_id + '/privileges'
    r = requests.get(url)
    if r.ok:
        data = r.json()
        if data['status']:
            return 1, data['message']
        else:
            return 0, data['message']
    else:
        return 2, r.content or r.reason


def get_project_members(user_id, project_id):
    s, members = get_member_in_project(user_id, project_id)
    if s:
        return []
    else:
        return members


def get_coproject_members(user_id, project_id):
    url = gen_url('members_in_coproject')
    params = {'user_id': user_id, 'project_id': project_id}
    r = requests.get(url, params=params)
    if r.ok:
        data = r.json()
        if data['status'] or not data['message']:
            print 'data: %s' % data
            return []
        else:
            return data['message']
    else:
        return []


def get_project_profile(project_id):
    url = gen_url('projects') + '/' + project_id
    r = requests.get(url)
    if r.ok:
        data = r.json()
        print 'project_profile: %s' % data
        if data['status']:
            return data['status'], data['message']
        elif not data['message']:
            return 1, data['message']
        else:
            return 0, data['message'].pop()
    else:
        return 2, r.content or r.reason


def get_delivery():
    url = gen_url('cdn_delivery')

    values = {
        'period_type': 'all',
    }
    r = requests.post(url, data=values)
    if r.ok:
        data = r.json()
        resp = data.pop('aggregations').pop('group_by_field').pop('buckets')
    else:
        resp = []
    return len(resp)


def get_project_statistics(user_id, project_id):
    url = gen_url('statistics')
    values = {
        'project_id': project_id,
        'user_id': user_id,
    }
    r = requests.get(url, params=values)
    if r.ok:
        data = r.json()
        if data['status']:
            resp = 1, data['message']
        else:
            data = data['message']
            data.update(distributeNum=get_delivery())
            resp = 0, data
    else:
        resp = 1, r.content or r.reason
    return resp


# Flavor
def list_flavors():
    url = gen_url('flavors')
    r = requests.get(url)
    if r.ok:
        data = r.json()
        status = 0 if data['status'] == 'ok' else 1
        resp = status, data['message']
    else:
        resp = 1, r.content or r.reason

    return resp


# Image
def list_images_by_os(cluster_id, os='linux'):
    url = gen_url('images_v2')
    r = requests.get(url, params={'os': os, 'cluster_id': cluster_id})
    if r.ok:
        data = r.json()
        status = 0 if data['status'] == 'ok' else 1
        return status, data['message']
    else:
        return 1, r.content


def get_image_info(image_id):
    url = gen_url('images_v2') + '/' + str(image_id)
    r = requests.get(url)
    if r.ok:
        data = r.json()
        status = 0 if data['status'] == 'ok' else 1
        return status, data['message']
    else:
        return 1, r.content


# IP
def pick_public_network(random_pick=True):
    s, m = db.sql('SELECT * FROM net WHERE public=1;')
    if s:
        raise Exception(str(m))
    elif not m:
        raise Exception('no public network available!')

    if random_pick:
        idx = random.randint(0, len(m)-1)
    else:
        idx = 0
    return m[idx]


def request_new_ip(cluster_id):
    url = gen_url('ip')
    r = requests.get(url, data={'cluster_id': cluster_id})
    print cluster_id
    print r.content
    if r.ok:
        result = r.json()
        if result['status']:
            return None
        else:
            return result['content']
    else:
        return None


def release_ip(ip):
    url = gen_url('ip') + '/' + ip
    r = requests.delete(url)
    if r.ok:
        result = r.json()
        if result['status']:
            return False
        else:
            return True
    else:
        return False


def _query_vm_detail(cluster_id, instance_id):
    url = '{0}/{1}'.format(gen_url('instance_query'), instance_id)
    values = {
        'cluster_id': cluster_id,
    }
    r = requests.get(url, params=values)
    if r.ok:
        return 0, r.json()
    else:
        return 1, r.content or r.reason


def push_log_all(data, action):
    try:
        log_api = gen_url('logs_add')
        value = [
            "instance_id",
            "user_id",
            "resource_id",
            "description"
        ]

        values = {"action": action}
        for i in value:
            if i in data:
                if i == "instance_id":
                    values.update(({"resource_id": data[i]}))
                else:
                    values.update({i: data[i]})
        print values
        r = requests.post(log_api, data=values)
        return r.json()
    except KeyError as ke:
        LOGGER.exception(ke.message)
        return None
    except Exception as e:
        LOGGER.exception(e.message)
        return None


# Key
def list_project_keypairs(project_id):
    url = gen_url('keypairs')
    params = {'project_id': project_id}

    r = requests.get(url, params=params)
    if r.ok:
        data = r.json()
        if data['status']:
            resp = 1, data['content']
        else:
            resp = 0, data['content']
    else:
        resp = 1, r.content or r.reason
    return resp


def create_single_keypair(project_id, cluster_id,
                          user_id, key_id, show_name):
    # url = '{0}/keypairs'.format(auth_get('globalIp'))
    url = gen_url('keypairs')
    values = {
        'cluster_id': cluster_id,
        'project_id': project_id,
        'user_id': user_id,
        'key_id': key_id,
        'show_name': show_name,
    }
    print values
    r = requests.post(url, data=values)
    if r.ok:
        LOGGER.debug('create key: %s', r.content)
        ret = r.json()
        if not ret['status']:
            print ret['status']
            return True, ret['content']
        else:
            return False, ret['content']
    else:
        return False, r.reason


def post_single_keypairs(project_id,
                         cluster_id,
                         user_id,
                         key_id,
                         key_content,
                         show_name):
    # url = '{0}/keypairs'.format(auth_get('globalIp'))
    url = gen_url('keypairs')
    values = {
        'cluster_id': cluster_id,
        'project_id': project_id,
        'user_id': user_id,
        'key_id': key_id,
        'key_content': key_content,
        'show_name': show_name,
    }
    r = requests.post(url, data=values)
    if r.ok:
        print "r == %s" % r.json()
        return r.json()
    else:
        return None


def get_project_keys_clusters_ids(project_id, key_id):
    s, m = db.get('keypairs',
                  where={'project_id': project_id, 'key_id': key_id})
    if s or not m:
        return None

    cluster_ids = [c['cluster_id'] for c in m]
    return cluster_ids


def get_cluster_ids_by_network(network_id):
    s, m = db.get('cluster_id', where={'network_id': network_id})
    if s or not m:
        return None
    cluster_ids = [c['cluster_id'] for c in m]
    return cluster_ids


def get_project_clusters_ids(project_id, support_vpc=False):
    pre_sql = (u'SELECT '
               ' c.id,'
               ' c.name,'
               ' c.idc_id,'
               ' c.detail '
               'FROM '
               ' project_cluster pc,'
               ' cluster c '
               'WHERE '
               ' pc.cluster_id=c.id '
               'AND '
               ' pc.project_id="%s" '
               'AND '
               ' c.vpc_enabled=%s;') % (project_id, int(support_vpc))
    LOGGER.debug('sql: %s', pre_sql)
    s, m = db.sql(pre_sql)
    if s or not m:
        return None
    cluster_ids = [c['id'] for c in m]
    return cluster_ids


def post_single_key(cluster_id, project_id, user_name,
                    key_id, show_name, key_content=None):
    url = gen_url('keypairs')
    data = {
        'cluster_id': cluster_id,
        'project_id': project_id,
        'user_id': user_name,
        'key_id': key_id,
        'key_content': key_content,
        'show_name': show_name,
    }
    r = requests.post(url, data=data)
    if r.ok:
        data = r.json()
        if data['status']:
            return 1, data['content']
        else:
            return 0, data['content']
    else:
        return 2, r.content or r.reason


def delete_single_key(cluster_id, project_id, key_id):
    url = gen_url('keypairs') + '/' + key_id
    data = {
        'cluster_id': cluster_id,
        'project_id': project_id,
    }
    r = requests.delete(url, data=data)
    if r.ok:
        data = r.json()
        return data['status'], data['content']
    else:
        return 2, r.content or r.reason


def delete_single_keypairs(project_id, key_id):
    cluster_ids = get_project_keys_clusters_ids(project_id, key_id)

    if cluster_ids is None:
        return 1, 'No cluster attached on this project!'
    else:
        ret_val = []
        for c_id in cluster_ids:
            s, m = delete_single_key(c_id, project_id, key_id)
            if s:
                ret_val.append([s, m])

        print '%s' % ret_val
        ret_val = dict(ret_val)

        if ret_val:
            return len(ret_val), '\n'.join(ret_val.values())
        else:
            return 0, '%s removed from project: %s!' % (key_id, project_id)


# User
def get_user_profile(user_name):
    url = gen_url('users_q')
    r = requests.get(url, params={'user_name': user_name})
    if r.ok:
        data = r.json()
        return data['status'], data['message']
    else:
        return 1, r.content or r.reason


def get_permission(user_name, project_id, privilege_name):
    url = gen_url('permission')
    params = {
        'privilege_name': privilege_name,
        'project_id': project_id,
        'user_id': user_name,
    }
    r = requests.get(url, params=params)
    if r.ok:
        data = r.json()
        print 'get_permission: %r' % data
        if data['status']:
            resp = 1, data['message']
        elif not data['message']:
            resp = 1, 'permission not found.'
        else:
            resp = 0, data['message'][0]
    else:
        resp = 1, r.content or r.reason

    return resp


def check_permission(user_name, project_id, privilege_name, permission):
    print 'username: ', user_name
    print 'project_id: ', project_id
    print 'privilege_name: ', privilege_name
    print 'search permission: ', permission

    s, perm = get_permission(user_name, project_id, privilege_name)
    print 'check perm: %r' % perm
    if s:
        return False

    if permission in perm['permission']:
        return True
    else:
        return False


def is_admin(user_name):
    s, m = get_user_profile(user_name)
    if s:
        return False
    elif not m:
        return False
    else:
        user_info = m[0]
        return user_info['is_admin'] == 1


def is_project_exists(project_id):
    s, m = db.one('project', where={'project_id': project_id})
    if s or not m:
        return False
    return True


def is_project_admin(user_name, project_id, only_creator=False):
    where = {
        'user_id': user_name,
        'project_id': project_id,
        'is_active': 1,
    }
    if only_creator:
        where.update(parent_id=user_name)
    s, m = db.one('user_project_role', where=where)
    LOGGER.debug('is_project_admin: %s, %s, where: %r', s, m, where)
    if s or not m:
        return False
    else:
        return m['role_id'] == 'admin'


def is_role_creator(role_id, user_name):
    where = {
        'role_id': role_id,
        'user_id': user_name,
    }
    s, m = db.one('role', where=where)
    if s or not m:
        return False
    else:
        return True


def switch_role_for_project_member(user_id, member_id,
                                   project_id, role_id):
    url = gen_url('project_member_mod')
    params = {
        'user_id': user_id,
        'project_id': project_id,
        'member_id': member_id,
        'role_id': role_id,
    }
    r = requests.post(url, data=params)
    if r.ok:
        data = r.json()
        if data['status'] == 0:
            return data['status'], u'切换角色成功!'
        elif data['message'] != 1:
            return 1, u'切换角色失败!'
        else:
            return data['status'], data['message']
    else:
        return 1, r.content or r.reason


def is_active(project_id, user_id):
    sql = (u'SELECT co.parent_id, co.is_active '
           'FROM '
           ' co_relation co, '
           ' project '
           'WHERE '
           ' co.parent_id=project.user_id '
           'AND '
           ' project.project_id="%s" '
           'AND '
           ' co.user_id="%s" '
           'LIMIT 1;' % (project_id, user_id))

    s, m = db.sql(sql)

    print 'is_active: %s, %s, %s' % (s, m, sql)
    if s or not m:
        return False

    if m[0]['parent_id'] == user_id:
        return True
    else:
        return m[0]['is_active'] == 1


def list_networks(project_id, cluster_id):
    pre_sql = (u'SELECT '
               ' net.name, '
               ' net.network_id, '
               ' net.project_id, '
               ' net.cluster_id, '
               ' net.cidr, '
               ' count(subnet.id) AS subnet_count, '
               ' count(route.id) AS route_count, '
               ' net.create_time, '
               ' net.creator '
               'FROM '
               ' `net`, '
               ' subnet, '
               ' route '
               'WHERE '
               ' net.network_id=subnet.network_id '
               'AND '
               ' route.subnet_id=subnet.subnet_id '
               'AND '
               ' net.project_id="%s" '
               'GROUP BY net.network_id;')
    sql = pre_sql % project_id
    LOGGER.debug('List network: %s', sql)
    s, m = db.sql(sql)
    return s, m


def show_network(cluster_id, network_id):
    s, token = get_token(cluster_id)
    if s:
        return s, token

    url = token['neutron'] + '/v2.0/networks/' + network_id
    headers = {
        'X-Auth-Token': token['x-auth-token'],
    }
    r = requests.get(url, headers=headers)
    if r.ok:
        return 0, r.json()
    else:
        return 1, r.content or r.reason


def delete_network(cluster_id, network_id):
    s, m = get_token(cluster_id)
    if s:
        return s, m
    token = m

    url = token['neutron'] + '/v2.0/networks/' + network_id
    headers = {
        'X-Auth-Token': token['x-auth-token'],
    }
    r = requests.delete(url, headers=headers)
    if r.ok:
        # return 0, r.json()
        return 0, r.content
    else:
        return 1, r.content or r.reason


def create_network(cluster_id, name, cidr, shared=False, external=False):
    s, m = get_token(cluster_id)
    if s:
        return s, m
    token = m

    url = token['neutron'] + '/v2.0/networks'
    headers = {
        'X-Auth-Token': token['x-auth-token'],
    }
    params = {
        'network': {
            'name': name,
            'admin_state_up': True,
            'shared': shared,
            'router:external': external,
        }
    }
    r = requests.post(url, json=params, headers=headers)
    if r.ok:
        return 0, r.json()['network']
    else:
        return 1, r.content or r.reason


def _gen_gateway(cidr):
    net = netaddr.IPNetwork(cidr)
    return str(net[1])


def _gen_ip_pool(cidr):
    net = netaddr.IPNetwork(cidr)
    return str(net[2]), str(net[-2])


def create_subnet(cluster_id, name,
                  cidr,
                  network_id,
                  gateway=None,
                  ip_pool=None,
                  ip_version=4):
    s, m = get_token(cluster_id)
    if s:
        return s, m
    token = m

    url = token['neutron'] + '/v2.0/subnets'
    headers = {
        'X-Auth-Token': token['x-auth-token'],
    }

    if gateway is None:
        gateway = _gen_gateway(cidr)

    if ip_pool is None:
        ip_pool = _gen_ip_pool(cidr)

    params = {
        'subnet': {
            'name': name,
            'network_id': network_id,
            'ip_version': ip_version,
            'cidr': cidr,
            'gateway_ip': gateway,
            'allocation_pools': [{'start': ip_pool[0], 'end': ip_pool[1]}],
        }
    }
    LOGGER.debug('create subnet: %s', params)
    r = requests.post(url, json=params, headers=headers)
    LOGGER.debug('subnet result: %s', r.content)
    if r.ok:
        return 0, r.json()['subnet']
    else:
        return 1, r.content or r.reason


def delete_subnet_from_cluster(cluster_id, subnet_id):
    s, m = get_token(cluster_id)
    if s:
        return s, m
    token = m

    url = token['neutron'] + '/v2.0/subnets/' + subnet_id
    headers = {
        'X-Auth-Token': token['x-auth-token'],
    }
    r = requests.delete(url, headers=headers)
    if r.ok:
        return 0, r.content
    else:
        return 1, r.content or r.reason


def delete_subnet(subnet_id, cluster_id):
    s, m = get_router_by_subnet(subnet_id)
    if s:
        return 1, m
    elif not m:
        return 1, 'no attached router.'

    _router = m
    # Remove router-interface.
    s, m = remove_router_interface(cluster_id, _router['router_id'], subnet_id)
    if s:
        LOGGER.warning('Remove router-interface: %s, %s', s, m)
        # return 2, m
    else:
        record_route_remove(_router['router_id'], subnet_id)
        LOGGER.info('Remove router-interface ok: %s, %s', s, m)

    # remove subnet
    s, m = delete_subnet_from_cluster(cluster_id, subnet_id)
    if not s:
        record_subnet_remove(subnet_id)

    return s, m


def list_subnets_in_project(project_id):
    pre_sql = (u'SELECT '
               ' subnet.subnet_id, '
               ' subnet.`name`, '
               ' subnet.cidr, '
               ' net.`network_id`, '
               ' net.`name` AS network_name, '
               ' net.project_id, '
               ' router.router_id, '
               ' router.router_name, '
               ' subnet.az, '
               ' count(route.id) AS route_count, '
               ' subnet.`status`, '
               ' subnet.create_time, '
               ' subnet.creator '
               'FROM '
               ' route, '
               ' router, '
               ' route_table rtb, '
               ' `subnet`, '
               ' `net` '
               'WHERE '
               ' subnet.subnet_id = route.subnet_id '
               'AND route.rtb_id = rtb.rtb_id '
               'AND rtb.router_id = router.router_id '
               'AND subnet.network_id = net.network_id '
               'AND net.project_id = "%s" '
               'GROUP BY subnet.subnet_id; ')
    sql = pre_sql % project_id
    LOGGER.debug('get subnets sql: %s', sql)
    s, m = db.sql(sql)
    return s, m


def list_networks_in_cluster(project_id, cluster_id):
    where = {
        'project_id': project_id,
        'cluster_id': cluster_id,
    }
    s, m = db.get('net', where=where)
    return s, m


def get_router_by_subnet(subnet_id):
    pre_sql = (u'SELECT '
               ' rt.router_id, '
               ' rt.router_name, '
               ' rt.project_id, '
               ' rt.cluster_id '
               ' FROM '
               ' `router` rt, '
               ' `route` r, '
               ' `route_table` rtb '
               'WHERE '
               ' r.rtb_id=rtb.rtb_id '
               'AND '
               ' rtb.router_id=rt.router_id '
               'AND '
               ' r.subnet_id="%s";')
    sql = pre_sql % subnet_id
    s, m = db.sql(sql)
    if s:
        return s, m
    elif not m:
        return 1, 'no records!'
    return s, m[0]


# def list_subnets(project_id, cluster_id):
#     """ List subnets in a cluster.
#     """
#     s, m = list_networks_in_cluster(project_id, cluster_id)
#     if s:
#         return 1, m

#     networks = m

#     subnets = []

#     for network in networks:
#         s, _subnets = list_subnets_in_network(network['network_id'])
#         if not s and _subnets:
#             subnets.extend(_subnets)

#     return 0, subnets


def show_subnet(cluster_id, subnet_id):
    s, token = get_token(cluster_id)
    if s:
        return s, token

    url = token['neutron'] + '/v2.0/subnets/' + subnet_id
    headers = {
        'X-Auth-Token': token['x-auth-token'],
    }
    r = requests.get(url, headers=headers)
    if r.ok:
        return 0, r.json()
    else:
        return 1, r.content or r.reason


# Router
def list_routers(project_id, cluster_id):
    where = {
        'project_id': project_id,
        'cluster_id': cluster_id,
    }
    return db.get('router', where=where)


def show_router(cluster_id, router_id):
    s, token = get_token(cluster_id)
    if s:
        return s, token

    url = token['neutron'] + '/v2.0/routers/' + router_id
    headers = {
        'X-Auth-Token': token['x-auth-token'],
    }
    r = requests.get(url, headers=headers)
    if r.ok:
        return 0, r.json()
    else:
        return 1, r.content or r.reason


def create_router(cluster_id, name, admin_state_up=True):
    s, m = get_token(cluster_id)
    if s:
        return s, m
    token = m
    url = token['neutron'] + '/v2.0/routers'
    headers = {
        'X-Auth-Token': token['x-auth-token'],
    }
    params = {
        'router': {
            'name': name,
            'admin_state_up': admin_state_up,
        }
    }
    r = requests.post(url, json=params, headers=headers)
    LOGGER.debug('create router result:%s, %s', r.status_code, r.content)
    if r.ok:
        return 0, r.json()['router']
    else:
        return 1, r.content or r.reason


# def router_ex_gateway_info(cluster_id, router_id):
#     s, m = get_token(cluster_id)
#     if s:
#         return s, m
#     token = m


def remove_router_interface(cluster_id, router_id,
                            subnet_id=None, port_id=None):
    s, m = get_token(cluster_id)
    if s:
        return s, m
    token = m

    if subnet_id is None and port_id is None:
        return 1, 'One of subnet_id/port_id must be specified!'

    url = token['neutron'] + '/v2.0/routers/' + router_id + '/remove_router_interface'

    headers = {
        'Content-Type': 'application/json',
        'X-Auth-Token': token['x-auth-token'],
    }

    params = {}
    if subnet_id:
        params['subnet_id'] = subnet_id

    if port_id:
        params['port_id'] = port_id

    r = requests.put(url, json=params, headers=headers)
    if r.ok:
        return 0, r.json()
    else:
        return 1, r.content or r.reason


def add_router_interface(cluster_id, router_id, subnet_id=None, port_id=None):
    s, m = get_token(cluster_id)
    if s:
        return s, m
    token = m

    if subnet_id is None and port_id is None:
        return 1, 'One of subnet_id/port_id must be specified!'

    url = token['neutron'] + '/v2.0/routers/' + router_id + '/add_router_interface'

    headers = {
        'Content-Type': 'application/json',
        'X-Auth-Token': token['x-auth-token'],
    }

    params = {}
    if subnet_id:
        params['subnet_id'] = subnet_id

    if port_id:
        params['port_id'] = port_id

    r = requests.put(url, json=params, headers=headers)
    if r.ok:
        return 0, r.json()
    else:
        return 1, r.content or r.reason


# Routes
def list_subnets_by_rtb_id(rtb_id):
    pre_sql = (u'SELECT '
               ' subnet_id '
               'FROM '
               ' route '
               'WHERE '
               ' rtb_id="%s";')
    sql = pre_sql % rtb_id
    LOGGER.debug('list subnets by rtb: %s', sql)
    return db.sql(sql)


def get_network_by_rbt(rtb_id):
    pre_sql = (u'SELECT '
               ' net.network_id, '
               ' net.name, '
               ' net.cidr '
               'FROM '
               ' net, route_table '
               'WHERE '
               ' net.network_id=route_table.network_id '
               'AND '
               ' rtb_id="%s" LIMIT 1;')
    sql = pre_sql % rtb_id
    LOGGER.debug('get network by rtb_id: %s', sql)
    s, m = db.sql(sql)
    if s:
        raise Exception(m)
    elif not m:
        # raise Exception('No such route table')
        LOGGER.error('No such route table: %s', rtb_id)
        return {}
    return m[0]


def list_routes(project_id):
    pre_sql = (u'SELECT '
               ' rtb.rtb_id, '
               ' rtb.name, '
               ' rtb.rtb_type as type, '
               ' rtb.creator, '
               ' rtb.create_time, '
               ' rtb.network_id, '
               ' net.`name` AS network_name, '
               ' rt.router_id, '
               ' rt.router_name, '
               ' rt.project_id, '
               ' rt.cluster_id '
               'FROM '
               ' `route_table` rtb, '
               ' `router` rt, '
               ' `subnet`, '
               ' `net` '
               'WHERE '
               ' rtb.router_id=rt.router_id '
               'AND '
               ' subnet.network_id=net.network_id '
               'AND '
               ' rt.project_id="%s" '
               'GROUP BY rtb.rtb_id;')
    sql = pre_sql % project_id
    LOGGER.debug('Get route list: %s', sql)
    err, results = db.sql(sql)
    if not err and results:
        for r in results:
            net = get_network_by_rbt(r['rtb_id'])
            if net:
                r.update({'network_name': net['name']})

    return err, results


def add_route(cluster_id, router_id, nexthop, destination):
    # Refs: `http://specs.openstack.org/openstack/neutron-specs/specs/api/extra_routes.html#update-extra-route`
    s, m = get_token(cluster_id)
    if s:
        return s, m
    token = m

    url = token['neutron'] + '/v2.0/routers/' + router_id
    headers = {
        'X-Auth-Token': token['x-auth-token'],
    }
    params = {
        'router': {
            'routes': [
                {
                    'nexthop': nexthop,
                    'destination': destination,
                }
            ]
        }
    }
    r = requests.put(url, json=params, headers=headers)
    if r.ok:
        return 0, r.json()
    else:
        return 1, r.content or r.reason


def get_subnets_by_network(network_id):
    pre_sql = (u'SELECT net.cluster_id, subnet.network_id, subnet.subnet_id, '
               'subnet.name, subnet.cidr, subnet.status, subnet.creator '
               'FROM net, subnet '
               'WHERE net.network_id=subnet.network_id '
               'AND net.network_id="%s";')
    sql = pre_sql % network_id
    LOGGER.debug('get subnets: %s', sql)
    s, m = db.sql(sql)
    return s, m


def get_project_by_router(router_id):
    """ Get project info by `router_id`
    """
    s, m = db.one('router', where={'router_id': router_id})
    if s:
        return 1, m
    else:
        data = {
            'project_id': m['project_id'],
            'cluster_id': m['cluster_id'],
        }
        return 0, data


def record(table, **values):
    return db.add(table, values=values)


def record_network(**values):
    return record('net', **values)


def record_subnet(**values):
    return record('subnet', **values)


def record_router(**values):
    return record('router', **values)


def record_route(**values):
    return record('route', **values)


def record_route_table(**values):
    return record('route_table', **values)


def record_network_remove(network_id):
    where = {
        'network_id': network_id,
    }
    return db.remove('net', where=where)


def record_subnet_remove(subnet_id):
    where = {
        'subnet_id': subnet_id,
    }
    return db.remove('subnet', where=where)


def record_route_remove(router_id, subnet_id):
    where = {
        'router_id': router_id,
        'subnet_id': subnet_id,
    }
    return db.remove('route', where=where)


def list_instance_interface(instance_id):
    url = gen_url('instance_interfaces').format(instance_id=instance_id)
    r = requests.get(url)
    if r.ok:
        resp = 0, r.json()
    else:
        resp = 1, r.content or r.reason
    return resp


def attach_interface(instance_id, net_id, fixed_ip=None):
    url = gen_url('attach_instance_interface').format(instance_id=instance_id)
    data = {
        'interfaceAttachment': {
            'net_id': net_id
        }
    }
    if fixed_ip:
        data['interfaceAttachment']['fixed_ips'] = [
            {
                'ip_address': fixed_ip
            }
        ]
    r = requests.post(url, data=data)
    if r.ok:
        resp = 0, r.json()
    else:
        resp = 1, r.content or r.reason
    return resp


def dettach_interface(instance_id, port_id):
    url = gen_url('detach_instance_interface').format(instance_id=instance_id, port_id=port_id)
    r = requests.delete(url)
    if r.ok:
        resp = 0, r.json()
    else:
        resp = 1, r.content or r.reason
    return resp


def sync_network_info(instance_id, cluster_id,
                      private_netname, public_netname=None,
                      loop_count=3, spacing=10):
    url = gen_url('instance_sync_network').format(instance_id=instance_id)
    # 'instance_sync_network': '/status_helper/instances/{instance_id}/sync_network_info',
    payload = {
        'cluster_id': cluster_id,
        'wait_time': spacing,
        'loop_count': loop_count,
        'private_network_name': private_netname,
    }
    if public_netname:
        payload['public_network_name'] = public_netname

    r = requests.post(url, data=payload)
    if r.ok:
        return 0, r.json()
    else:
        return r.status_code, r.content or r.reason
